"""
@Time : 2023/12/20 17:33
@Auth : Mr.Zhao
"""

# 面向对象封装, 减少代码冗余，降低耦合性
import re
import time

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.wait import WebDriverWait
from common.Logging import logger

from common.ying import Chaojiying_Client
from common.all_path import path
from common.Relation import relation


class MyWeb:
    def __init__(self):
        self.driver: webdriver.Edge = None
        self.relations_dict: dict = {}

    def openBrower(self):
        option = webdriver.EdgeOptions()  # 不另外导包也行
        option.add_experimental_option('detach', True)

        # 去掉自动化标识（很多网站会通过js检测是否为自动化操控）
        option.add_experimental_option('excludeSwitches', ['enable-automation'])
        option.add_argument('--disable-blink-features=AutomationControlled')

        # 关闭密码弹窗
        prefs = {}
        prefs['credentials_enable_service'] = False
        prefs['profile.password_manager_enabled'] = False
        option.add_experimental_option('prefs', prefs)

        option.add_argument(r'--user-data-dir=D:\Pycharm\edge_dir\edge_dir2')  # 使用初始化后的浏览器配置文件
        self.driver = webdriver.Edge(options=option)
        self.driver.implicitly_wait(10)
        self.driver.maximize_window()

    def getUrl(self, url: str = ''):
        """
        页面跳转
        :param url: get参数里url必须以http开头，这里进行一个非标准的兼容
        :return:
        """
        if not url.startswith('http'):
            url = 'http://' + url
        self.driver.get(url)

    def __find_lo(self, lo: str = ''):
        """
        只支持'xpath', 'id', 'css' 定位
            xpath一般是/ or (开头
            css #开头或者包含>
            id一般是纯英文单词
        :param lo: location
        :return:定位错误或者未找到返回Node
        """
        if lo.startswith('/') or lo.startswith('('):
            ele = self.driver.find_element('xpath', lo)
        elif lo.__contains__('>') or lo.startswith('#'):
            ele = self.driver.find_element('css selector', lo)
        else:
            ele = self.driver.find_element('id', lo)
        #操作元素的颜色高亮显示
        self.driver.execute_script('arguments[0].style.background = "greenyellow"', ele)
        #self.driver.execute_script('arguments[0].style.background = "#adff2f"', ele)
        return ele

    @relation
    def click(self, lo: str = ''):
        """
        元素的点击操作
        :param lo: location
        :return:
        """
        ele = self.__find_lo(lo)
        ele.click()
        return True

    @relation
    def input(self, lo: str = '', val: str = ' '):
        """
        输入
        :param lo: 支持xpath, id, css selector
        :param val:
        :return:
        """
        # 兼容图片上传
        ele = self.__find_lo(lo)
        if val.endswith('.jpg') or val.endswith('.png'):
            val = path + "lib\\" + val
        ele.send_keys(val)

    def getAtt(self, lo: str = '', type: str = ''):
        """
        获取元素的type属性
        :param lo: xpath, class ,id
        :param type: 属性
        :return:
        """
        ele = self.__find_lo(lo)
        value = ele.get_attribute(type)
        self.relations_dict['att'] = value
        return value

    def tryclicks(self, lo: str = ''):
        """
        尝试点击弹窗（存在则点击，不存在不操作）
        :param lo: location
        :return:
        """
        try:
            ele = self.__find_lo(lo)
            ele.click()
            return True
        except:
            return False


    def getText(self, lo: str = '', patt: str = ''):
        """
        获取text，返回需要的值，支持正则匹配
        :param lo: ...
        :param patt: 匹配模式
        :return:
        """
        ele = self.__find_lo(lo)
        text = ele.text
        if patt:
            ret = re.findall(patt, text)
            # 可能匹配失败, 此时ret为None,如果匹配成功则返回第一个匹配项
            if ret:
                text = ret[0]
            else:
                text = None
        self.relations_dict['text'] = text #存储
        return text

    @relation
    def assert_contains(self, act_res:str = '', exp_res:str = ''):
        """
        断言，判断实际结果是否包含预期结果
        :param act_res: 实际结果（一般由内部字典存储）
        :param exp_res: 预期结果
        :return:
        """
        logger.info(f'实际结果是{act_res},  期望值是{exp_res}')
        if act_res.__contains__(exp_res):
            return True
        else:
            assert act_res.__contains__(exp_res)
    def ref(self):
        """
        刷新页面
        :return:
        """
        self.driver.refresh()

    def getTitle(self):
        """
        获取标题
        :return:
        """
        title = self.driver.title
        self.relations_dict['title'] = title
        return title

    def hover(self, lo: str = ''):
        """
        鼠标悬浮
        :param lo:
        :return:
        """
        ele = self.__find_lo(lo)
        action = ActionChains(self.driver)
        action.move_to_element(ele).perform()
        return True

    def clear(self, lo: str = ''):
        """
        指定输入框的清空
        :param lo:
        :return:
        """
        ele = self.__find_lo(lo)
        ele.clear()
        return True

    def inIframe(self, lo: str = ''):
        """
        进入iframe
        :param lo:...
        :return:
        """
        ele = self.__find_lo(lo)
        self.driver.switch_to.frame(ele)
        return True

    def outIframe(self):
        """
        退出所有iframe
        :return:
        """
        self.driver.switch_to.default_content()
        return True

    def wait(self, lo: str = '', attr: str = '', val: str = ''):
        """
        显示等待
        :param lo: location
        :param attr: 属性
        :param val: 值
        :return:
        """
        if lo.startswith('/') or lo.startswith('('):
            ele = ('xpath', lo)
        elif lo.__contains__('>') or lo.startswith('#'):
            ele = ('css selector', lo)
        else:
            ele = ('id', lo)
        WebDriverWait(self.driver, 10, 0.5).until(EC.text_to_be_present_in_element_attribute(ele, attr, val))
        return True

    def select(self, lo: str = '', value: str = ''):
        """
        下拉框选择，不支持value属性的获取（用户不可见属性）
        :param lo:
        :param value: 支持index和text获取
        :return:
        """
        ele = self.__find_lo(lo)
        chose = Select(ele)
        try:
            value = int(value)
            chose.select_by_index(value)
        except:
            chose.select_by_visible_text(value)

    def sleep(self, val: str):
        """
        强制等待
        :param val: int(val) s
        :return:
        """
        time.sleep(int(val))
        return True

    def quit(self):
        """退出当前浏览器"""
        self.driver.quit()
        self.driver = None
        return True

    def switchWin(self, val: str = ''):
        """
        跳转到指定页面
        :param val: 如果为int值则通过下标跳转，否则认为val就是跳转后的title，按照title跳转
        :return:
        """
        handles = self.driver.window_handles
        try:
            # 下标跳转
            val = int(val)
            self.driver.switch_to.window(handles[val])
            return True
        except:
            # title查找跳转
            for i in handles:
                self.driver.switch_to.window(i)
                if self.driver.title == val:
                    break
            return True

    @relation
    def saveDict(self, key: str = '', val: str = ''):
        """
        关键字添加
        :param key:
        :param val:
        :return:
        """
        # if key and val:
        self.relations_dict[key] = val
        return self.relations_dict

    def close(self):
        """
        关闭当前页面
        :return:
        """
        self.driver.close()
        return True

    @relation
    def getVerify(self, lo: str = '', lo2: str = '', lo3: str = ''):
        """
        获取验证码，并输入到输入框，点击登录，持续该操作，直到破解成功
        :param lo: 验证码图片location
        :param lo2: 输入框location
        :param lo3: 登录按钮 location
        :return:
        """
        # 登录后跳转，之后找不到验证码图片，如果能找到，说明登录失败
        while True:
            try:
                time.sleep(2)
                ele = self.__find_lo(lo)
            except:
                # 登录成功
                break
            ele.screenshot('./verify.png')
            ying = Chaojiying_Client()
            code = ying.PostPic('./verify.png')
            print(code)
            # self.relations_dict['verify'] = code #实现关联--保存关键字
            self.saveDict('verify', code)
            self.input(lo2, code)
            self.click(lo3)
